﻿using System;
using Birkelund.QueryStrings.Internals;

namespace Birkelund.QueryStrings.Tokens
{
    /// <summary>
    /// Class to support primitive datatypes plus <see cref="TimeSpan"/>, <see cref="Guid"/>, <see cref="DateTime"/> and enumerations
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class ValueTypeToken<T> : QueryStringTokenBase<T> where T : struct
    {
        /// <summary>
        /// Determine how an enum member should be rendered in querystring
        /// </summary>
        public EnumRenderMode EnumRenderMode { get; set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="ValueTypeToken&lt;T&gt;"/> class.
        /// </summary>
        /// <param name="keyName">The key.</param>
        /// <exception cref="System.InvalidOperationException">If invalid type</exception>
        public ValueTypeToken(string keyName)
            : base(keyName)
        {
            if (!Common.IsValidValueTypeToken(typeof(T)))
            {
                throw new InvalidOperationException("Only use primitive types/enums/DateTime/TimeSpan/Guid");
            }
            EnumRenderMode = EnumRenderMode.Numeric;
        }

        /// <summary>
        /// Parses from string.
        /// </summary>
        /// <param name="stringValue">The string value.</param>
        /// <returns></returns>
        protected override T OnParseFromString(string stringValue)
        {
            return Common.Cast<T>(stringValue);
        }

        /// <summary>
        /// Renders the token value
        /// </summary>
        /// <returns>
        /// Returns the rendered value.
        /// </returns>
        protected override string OnRenderTokenValue(T value)
        {
            Type currentType = typeof(T);
            if (currentType.IsEnum)
            {
                switch (EnumRenderMode)
                {
                    case EnumRenderMode.Numeric:
                        return Enum.Format(currentType, value, "D"); //D makes the enum into a numeric value
                    case EnumRenderMode.String:
                        return Enum.Format(currentType, value, "G");
                    case EnumRenderMode.Hex:
                        return Enum.Format(currentType, value, "X");
                    default:
                        throw new ArgumentOutOfRangeException();
                }
                
            }
            else if (currentType.IsPrimitive)
            {
                return Convert.ToString(value);
            }
            else
            {
                return Convert.ToString(value);
            }
        }
    }
}